#ifndef __LOG__
#define __LOG__

#include<iostream>
#include<thread>
#include<chrono>
#include<mutex>
#include<string>
#include<atomic>

#include<cstring>
#include<cassert>
#include<cstdarg>
#include<cstdio>
#include<ctime>
#include<time.h>

#include"log_queue.hpp"

class Log
{
public:
    //日志等级
    typedef enum
    {
        ERROR_LOG_LEVEL,
        WARN_LOG_LEVEL,
        INFO_LOG_LEVEL,
        DEBUG_LOG_LEVEL,
        NUM_LOG_LEVELS
    }log_level;

    const char* log_level_name[NUM_LOG_LEVELS]={
        "[ERROR]","[WARN ]","[INFO ]","[DEBUG]"
    };

    static log_level get_log_level();

    static log_level set_log_level(log_level level);

public:
    //日志时间
    //为什么就年是int类型？
    typedef struct my_time
    {
        int year;
        char month;
        char day;
        char hour;
        char minute;
        char second;
    }my_time;

    static my_time get_current_time()
    {
        auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
        tm *ptm = localtime(&time);
        Log::my_time t = {
            ptm->tm_year + 1900,
            static_cast<char>(ptm->tm_mon + 1), //+1原因：结构中月份为0-11
            static_cast<char>(ptm->tm_mday),
            static_cast<char>(ptm->tm_hour),
            static_cast<char>(ptm->tm_min),
            static_cast<char>(ptm->tm_sec)};
        return t;
    }

public:
    //设计日志类为单例模式
    static Log* get_instance()
    {
        static Log instance;
        return &instance;
    }

    //初始化日志类
    bool init(const char* file_name,int buffer_queue_size=0,log_level level=INFO_LOG_LEVEL,
              int buffer_size=8192,int split_lines=5000);

    bool is_init();
    
    //写入日志
    void write_log(const char* file_name,const char* tn_callbackname,int line_no,
                   log_level level,const char* message,...);

    //刷新缓冲区
    void flush();

public:
    static void async_flush()
    {
        get_instance()->async_write();
    }

private:
    Log();

    Log(const Log&) = delete;

    ~Log();

    //异步线程输入日志
    //使用单独线程向队列读取文件
    //当写入线程没有内容写入队列时，会进入睡眠状态，需要notify唤醒线程
    //当读取线程被意外唤醒，循环会被终止，这种情况不被允许，所以加了一个flag控制循环
    void* async_write();

private:
    long long _log_count;                               //日志数量
    bool _log_inited=false;                             //日志是否初始化
    bool _log_stdout=false;                             //日志输出模式，true为输出屏幕，false为输出文件
    FILE* _log_fp;                                      //日志输出文件

    char _log_dir_name[128];                            //日志输出文件目录名
    char _log_file_name[128];                           //日志输出文件名

    int _log_today;                                     //日志当天日期

    bool _log_is_async=false;                           //日志输出为同步还是异步
    Log_buffer_queue<std::string>* _log_queue=nullptr;  //异步缓冲区
    std::thread* _log_async_w_thread=nullptr;           //异步线程
    std::atomic<bool> _log_thread_stop{false};          //异步线程是否停止

    int _log_buf_size;                                  //缓存区大小
    char* _log_buf=nullptr;                             //缓冲区
    int _log_split_lines;                               //日志文件最大行数

    std::mutex _log_mutex;
};

extern Log::log_level cur_log_level;

inline Log::log_level Log::get_log_level()
{
    return cur_log_level;
}

inline Log::log_level Log::set_log_level(Log::log_level level)
{
    assert(level >= ERROR_LOG_LEVEL && level <= DEBUG_LOG_LEVEL);
    log_level old_level = cur_log_level;
    cur_log_level = level;
    return old_level;
}

#define DBG(message, ...)                                               \
    do{                                                                 \
        if(!Log::get_instance()->is_init())                             \
            DBG_LOG("logger must be inited before user!\n");            \
        if(Log::DEBUG_LOG_LEVEL<=Log::get_log_level())                  \
        {                                                               \
            Log::get_instance()->write_log(__FILE__, __FUNCTION__,      \
                __LINE__, Log::DEBUG_LOG_LEVEL, message, ##__VA_ARGS__);\
            Log::get_instance()->flush();                               \
        }                                                               \
    } while(0)

#define INF(message, ...)                                               \
    do{                                                                 \
        if(!Log::get_instance()->is_init())                             \
            DBG_LOG("logger must be inited before user!\n");            \
        if(Log::INFO_LOG_LEVEL<=Log::get_log_level())                   \
        {                                                               \
            Log::get_instance()->write_log(__FILE__, __FUNCTION__,      \
                __LINE__, Log::INFO_LOG_LEVEL, message, ##__VA_ARGS__); \
            Log::get_instance()->flush();                               \
        }                                                               \
    } while(0)

#define WAR(message, ...)                                               \
    do{                                                                 \
        if(!Log::get_instance()->is_init())                             \
            DBG_LOG("logger must be inited before user!\n");            \
        if(Log::WARN_LOG_LEVEL<=Log::get_log_level())                   \
        {                                                               \
            Log::get_instance()->write_log(__FILE__, __FUNCTION__,      \
                __LINE__, Log::WARN_LOG_LEVEL, message, ##__VA_ARGS__); \
            Log::get_instance()->flush();                               \
        }                                                               \
    } while(0)

#define ERR(message, ...)                                               \
    do{                                                                 \
        if(!Log::get_instance()->is_init())                             \
            DBG_LOG("logger must be inited before user!\n");            \
        if(Log::ERROR_LOG_LEVEL<=Log::get_log_level())                  \
        {                                                               \
            Log::get_instance()->write_log(__FILE__, __FUNCTION__,      \
                __LINE__, Log::ERROR_LOG_LEVEL, message, ##__VA_ARGS__);\
            Log::get_instance()->flush();                               \
        }                                                               \
    } while(0)

#endif